﻿using System;
using System.Collections;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Resources;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Security.Policy;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web;
using Microsoft.Win32;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Concurrent;
using System.Text.RegularExpressions;


namespace OF.Moniter
{
    public class Program2
    {
        static void Main2(string[] args)
        {
        }

        
        private static void Test()
        {
            int th = 0;
            int rowNumber = 0;
            List<DumpItem> list1 = new List<DumpItem>();
            list1.Add(new DumpItem { FileName = "List1", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 0 });
            list1.Add(new DumpItem { FileName = "List1", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 1 });
            list1.Add(new DumpItem { FileName = "List1", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 2 });
            list1.Add(new DumpItem { FileName = "List1", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 3 });

            th = 0;
            rowNumber = 1;
            List<DumpItem> list2 = new List<DumpItem>();
            list2.Add(new DumpItem { FileName = "list2", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 0 });
            list2.Add(new DumpItem { FileName = "list2", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 1 });
            list2.Add(new DumpItem { FileName = "list2", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 2 });

            th = 0;
            rowNumber = 0;
            List<DumpItem> list3 = new List<DumpItem>();
            list3.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 0 });
            list3.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 1 });
            list3.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 2 });
            list3.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 3 });
            list3.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 4 });
            list3.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 5 });


            th = 0;
            rowNumber = 0;
            List<DumpItem> list4 = new List<DumpItem>();
            list4.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 0 });
            list4.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 1 });
            list4.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 2 });
            list4.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (++rowNumber), RowNumber = 3 });
            list4.Add(new DumpItem { FileName = "list3", ThreadId = th, MethodKey = "Method" + (rowNumber++), RowNumber = 4 });

            List<List<DumpItem>> dumpFileLists = new List<List<DumpItem>>();
            dumpFileLists.Add(list1);
            dumpFileLists.Add(list2);
            dumpFileLists.Add(list3);
            dumpFileLists.Add(list4);

            AnalysisDumpPath(dumpFileLists);
        }

        private static void AnalysisDumpPath(List<List<DumpItem>> dumpFileLists)
        {
            List<int> fileIndexList = new List<int>();
            for (int i1 = 0; i1 < dumpFileLists.Count; i1++)
            {
                fileIndexList.Add(i1);
            }

            List<DumpItemMethodGroupKey> statDumpList = StatDumpItemList(null, dumpFileLists, 0, fileIndexList, new DiffFileSameTheadSameMethodCompare(),
                DiffFileSameTheadSameMethodSelector);
            List<List<DumpStatPath>> allPath = new List<List<DumpStatPath>>();
            List<DumpStatPath> path = new List<DumpStatPath>();
            GetAllStatPath(allPath, path, statDumpList);
            List<List<DumpStatPath>> orderList = allPath.OrderByDescending(list => DumpStatPath.GetPower(list)).ToList();


            var groupList = orderList.SelectMany(item => item).GroupBy(item => item.GroupKey.Substring(0, item.GroupKey.IndexOf(","))).OrderByDescending(item => item.Count()).ToList();
            foreach(var item in groupList)
            {
                Console.WriteLine("  " + item.Count() + ":" + item.Key);
            }

            Console.WriteLine("----------------------Get Service result:------------------------------------------");
            Dictionary<int, int> serviceDict = new Dictionary<int, int>();
            for (int listI = 0; listI < orderList.Count; listI++)
            {
                var list = orderList[listI];
                var ct = list.Where(item => item.GroupKey.IndexOf("Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp", 0) >= 0).Count();
                serviceDict.Add(listI, ct);
            }
            foreach (var kv in serviceDict.OrderByDescending(item => item.Value))
            {
                var list = orderList[kv.Key];
                Console.WriteLine("line " + kv.Key + ",ct:" + kv.Value);
                foreach (var item in list)
                {
                    Console.Write("  " + item.GroupKey + ":" + item.FileCount + ":" + string.Join(",", item.FileIndexList) + "\t\t");
                }
                Console.WriteLine();
                Console.WriteLine();
            }
            
            Console.WriteLine("--------------------------Group by request and Method:--------------------------------------");
            for (int listI = 0; listI < orderList.Count; listI++)
            {
                var list = orderList[listI];
                Console.WriteLine("line:" + listI);
                foreach (var item in list)
                {
                    Console.Write("  " + item.GroupKey + ":" + item.FileCount + ":" + string.Join(",", item.FileIndexList) + "\t\t");
                }
                Console.WriteLine();
                Console.WriteLine();
            }
        }

        private static void GetAllStatPath(List<List<DumpStatPath>> allPath, List<DumpStatPath> path, List<DumpItemMethodGroupKey> statDumpList)
        {
            foreach (var item in statDumpList)
            {
                var newPath = path.ToList();
                newPath.Add(new DumpStatPath{ GroupKey = item.GroupKey, FileCount = item.FileIndexList.Count, FileIndexList = item.FileIndexList });
                if(item.NextLevelGroup == null || item.NextLevelGroup.Count == 0)
                {
                    allPath.Add(newPath);
                }
                else if (item.NextLevelGroup.Count >= 1)
                {
                    GetAllStatPath(allPath, newPath, item.NextLevelGroup);
                }
            }
        }

        public class DumpStatPath
        {
            public string GroupKey
            { get; set; }

            public int FileCount
            { get; set; }

            public List<int> FileIndexList
            { get; set; }

            public static int GetPower(List<DumpStatPath> pathList)
            {
                int power = 0;
                int powerLevel = 1;
                foreach (DumpStatPath path in pathList)
                {
                    power += powerLevel * path.FileCount;
                    powerLevel *= 2;
                }
                return power;
            }
        }

        private static List<DumpItemMethodGroupKey> StatDumpItemList(int? thId, List<List<DumpItem>> dumpFileLists, int methodDepth, List<int> fileIndexList, IEqualityComparer<DumpItem> groupCompare,
            Func<IGrouping<DumpItem, DumpItem>, DumpItemMethodGroupKey> selectGroup)
        {
            List<DumpItemMethodGroupKey>  currentList = null;
            List<DumpItem> toGroupMethodList = new List<DumpItem>();
            for (int fileIndexI = 0; fileIndexI < fileIndexList.Count; fileIndexI++)
            {
                int fileIndex = fileIndexList[fileIndexI];
                List<DumpItem> dumpFile = dumpFileLists[fileIndex];
                foreach (var currentItem in dumpFile.Where(item => (thId == null || item.ThreadId == thId.Value) && item.RowNumber == methodDepth))
                {
                    toGroupMethodList.Add(currentItem);
                    currentItem.FileIndex = fileIndex;
                }
            }

            if (toGroupMethodList.Count > 0)
            {
                currentList = toGroupMethodList.GroupBy(item => item, groupCompare).Select(item => selectGroup(item)).ToList();
                foreach (var groupKey in currentList)
                {
                    groupKey.NextLevelGroup = StatDumpItemList(groupKey.ThreadId, dumpFileLists, methodDepth + 1, groupKey.FileIndexList, groupCompare, selectGroup);
                }
            }
            return currentList;
        }
        
        public static DumpItemMethodGroupKey DiffFileSameTheadSameMethodSelector(IGrouping<DumpItem, DumpItem> group)
        {
            return new DumpItemMethodGroupKey {
                ThreadId = group.Key.ThreadId,
                GroupKey = group.Key.MethodKey + "," + group.Key.RowNumber + "(" + group.Key.ThreadId + ")",
                NextLevelGroup = null,
                FileIndexList = group.Select(item => item.FileIndex).ToList()
            };
        }

        public class DiffFileSameTheadSameMethodCompare : IEqualityComparer<DumpItem>
        {
            public bool Equals(DumpItem x, DumpItem y)
            {
                return x.ThreadId.Equals(y.ThreadId) && x.MethodKey.Equals(y.MethodKey) && x.RowNumber.Equals(y.RowNumber);
            }

            public int GetHashCode(DumpItem obj)
            {
                return obj.ThreadId.GetHashCode() ^ obj.MethodKey.GetHashCode() ^ obj.RowNumber.GetHashCode();
            }
        }

        public class DumpItemMethodGroupKey
        {
            public int ThreadId
            { get; set; }

            public string GroupKey
            { get; set; }

            public List<int> FileIndexList
            { get; set; }

            public List<DumpItemMethodGroupKey> NextLevelGroup
            { get; set; }
        }
        

        public class DumpItem
        {
            public string FileName;
            public int ThreadId;
            public string MethodKey;
            public int RowNumber;

            public int FileIndex;
        }

       
        private static void testCache()
        {
            MethodCallCacheHelper cacheHelper = new MethodCallCacheHelper();
            cacheHelper.call(t12);
            cacheHelper.call(t12, new Tuple<int>(1));
            cacheHelper.call(t12, new Tuple<int>(1));
            cacheHelper.call(t12, new Tuple<int>(2));
            cacheHelper.call(t12, new Tuple<int>(3));
            cacheHelper.call(t12, new Tuple<int>(2));

            cacheHelper.call(t12, new Test1(1, 2 ));
            cacheHelper.call(t12, new Test1(1, 0));
            cacheHelper.call(t12, new Test1(0, 2));
            Func<Test1, int> test2 = t12;
            Func<Test1, int> test3 = test2;
            cacheHelper.call(test3, new Test1(1, 2));

            cacheHelper.call(t12, new Tuple<string>("123"));
            cacheHelper.call(t12, new Tuple<string>("1234"));
            cacheHelper.call(t12, new Tuple<string>("1234"));

            cacheHelper.call(t12<Tuple<string>>, new Tuple<string>(null as string));
            cacheHelper.call(t12<Tuple<string>>, new Tuple<string>("456"));
            cacheHelper.call(t12<Tuple<string>>, new Tuple<string>("4567"));
            cacheHelper.call(t12<Tuple<string>>, new Tuple<string>("456"));
            cacheHelper.call(t12<Tuple<string>>, new Tuple<string>(null as string));
            cacheHelper.call(t12<Tuple<string>>, new Tuple<string>(string.Empty));

            cacheHelper.call(t13);
            cacheHelper.call(t13, new Tuple<int>(1));
            cacheHelper.call(t13<Tuple<string>>, new Tuple<string>("789"));
            Console.ReadLine();
        }

        public class MethodCallCacheHelper
        {
            private ConcurrentDictionary<MethodCallDescrib, object> callResultDict = new ConcurrentDictionary<MethodCallDescrib, object>();
            public object call<T, R>(Func<T, R> method, T param) where T : IStructuralEquatable
            {
                if (!method.Method.IsStatic)
                {
                    throw new Exception("only support for static method!");
                }

                MethodCallDescrib describ = new MethodCallDescrib(method.Method, param);
                object result = null;
                if (callResultDict.TryGetValue(describ, out result))
                {
                    //Console.WriteLine("return cache:" + param);
                    return result;
                }
                else
                {
                    R obj = method(param);
                    callResultDict.TryAdd(describ, obj);
                    //Console.WriteLine("invoke:" + param);
                    return obj;
                }
            }

            public object call<R>(Func<R> method)
            {
                if (!method.Method.IsStatic)
                {
                    throw new Exception("only support for static method!");
                }

                MethodCallDescrib describ = new MethodCallDescrib(method.Method, null);
                object result = null;
                if (callResultDict.TryGetValue(describ, out result))
                {
                    //Console.WriteLine("return cache call:");
                    return result;
                }
                else
                {
                    R obj = method();
                    callResultDict.TryAdd(describ, obj);
                    //Console.WriteLine("return call:");
                    return obj;
                }
            }
        }

        public class MethodCallDescrib : Tuple<MethodInfo, IStructuralEquatable>
        {
            public MethodInfo MethodInfo;
            public IStructuralEquatable Param;

            public MethodCallDescrib(MethodInfo MethodInfo, IStructuralEquatable Param)
                : base(MethodInfo, Param)
            {
                this.MethodInfo = MethodInfo;
                this.Param = Param;
            }
        }
       
        static int t12(int a)
        {
            return 3;
        }

        public class Test1 : Tuple<int, int>
        {
            public int A;
            public int B;
            public Test1(int A, int B) : base(A, B)
            {
                this.A = A;
                this.B = B;
            }
        }

        static int t12(Test1 test1)
        {
            return 4;
        }

        static int t12(string a)
        {
            return 5;
        }

        static int t12<T>(T a)
        {
            return 6;
        }

        static int t12()
        {
            return 7;
        }

        static int t13()
        {
            return 8;
        }

        static int t13<T>(T a)
        {
            return 9;
        }

        static int t13(int a)
        {
            return 10;
        }
      
    }
}